// Copyright (c) 2023 Huawei Device Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// TODO: Serialization.

/// [Extensible Markup Language (XML) 1.0 (Fifth Edition)](https://www.w3.org/TR/xml/)

/// `Char` `::=` `#x9` | `#xA` | `#xD` | `[#x20-#xD7FF]` | `[#xE000-#xFFFD]` |
/// `[#x10000-#x10FFFF]`
pub(crate) fn is_char(ch: char) -> bool {
    ch == '\u{9}'
        || ch == '\u{A}'
        || ch == '\u{D}'
        || ('\u{20}'..='\u{D7FF}').contains(&ch)
        || ('\u{E000}'..='\u{FFFD}').contains(&ch)
        || ('\u{10000}'..='\u{10FFFF}').contains(&ch)
}

/// `VersionNum` `::=` `'1.'` `[0-9]+`
pub(crate) fn is_version_num(str: &str) -> bool {
    &str[0..2] == "1." && str[2..].chars().all(|ch| ch.is_ascii_digit())
}

/// `Name` `::=` `NameStartChar` `(NameChar)*`
#[allow(dead_code)]
pub(crate) fn is_name(str: &str) -> bool {
    !str.is_empty() && str.starts_with(is_name_start_char) && str[1..].chars().all(is_name_char)
}

/// `NameStartChar` `::=` `":"` | `[A-Z]` | `"_"` | `[a-z]` | `[#xC0-#xD6]` |
/// `[#xD8-#xF6`]                             | `[#xF8-#x2FF]` | `[#x370-#x37D]`
/// | `[#x37F-#x1FFF]`                             | `[#x200C-#x200D]` |
/// `[#x2070-#x218F]` | `[#x2C00-#x2FEF]`                             |
/// `[#x3001-#xD7FF]` | `[#xF900-#xFDCF]` | `[#xFDF0-#xFFFD]`                   
/// | `[#x10000-#xEFFFF]`
pub(crate) fn is_name_start_char(ch: char) -> bool {
    ch == ':'
        || ch.is_ascii_uppercase()
        || ch == '_'
        || ch.is_ascii_lowercase()
        || ('\u{C0}'..='\u{D6}').contains(&ch)
        || ('\u{D8}'..='\u{F6}').contains(&ch)
        || ('\u{0F8}'..='\u{2FF}').contains(&ch)
        || ('\u{370}'..='\u{37D}').contains(&ch)
        || ('\u{037F}'..='\u{1FFF}').contains(&ch)
        || ('\u{200C}'..='\u{200D}').contains(&ch)
        || ('\u{2070}'..='\u{218F}').contains(&ch)
        || ('\u{2C00}'..='\u{2FEF}').contains(&ch)
        || ('\u{3001}'..='\u{D7FF}').contains(&ch)
        || ('\u{F900}'..='\u{FDCF}').contains(&ch)
        || ('\u{FDF0}'..='\u{FFFD}').contains(&ch)
        || ('\u{10000}'..='\u{EFFFF}').contains(&ch)
}

/// `NameChar` `::=` `NameStartChar` | `"-"` | `"."` | `[0-9]` | `#xB7`
///                                  | `[#x0300-#x036F]` | `[#x203F-#x2040]`
pub(crate) fn is_name_char(ch: char) -> bool {
    is_name_start_char(ch)
        || ch == '-'
        || ch == '.'
        || ch.is_ascii_digit()
        || ch == '\u{B7}'
        || ('\u{0300}'..='\u{036F}').contains(&ch)
        || ('\u{203F}'..='\u{2040}').contains(&ch)
}

#[cfg(test)]
mod ut_grammar {
    use crate::grammar::*;

    /*
     * @title  UT test for char check
     * @design Condition Coverage
     * @precon NA
     * @brief  1. Call is_char.
     *         3. Compare result
     * @expect 1. true。
     * @auto   yes
     */
    #[test]
    fn ut_grammar_is_char() {
        let res = is_char('\u{E000}');
        assert!(res);

        let res = is_char('\u{10000}');
        assert!(res);
    }

    /*
     * @title  UT test for str check
     * @design Condition Coverage
     * @precon NA
     * @brief  1. Call is_name.
     *         3. Compare result
     * @expect 1. true。
     * @auto   yes
     */
    #[test]
    fn ut_grammar_is_name() {
        let res = is_name("Name");
        assert!(res);
    }
}
